﻿Imports System.Reflection
Imports System.Web.UI
Imports System.Security

Namespace AboutDNN.DNNMVC

    Public Class RouteHelper
        'Store the sub calss which extend this framework,
        'Action functions store in this assembly
        Public Shared ActionAssembly As Assembly


        ''' <summary>
        ''' Probes the different view paths to find the control
        ''' 1. Controller and Action empty = home/default.ascx
        ''' 2. Controller empty = home/
        ''' 3. Action empty = default.ascx
        ''' 4. If Action is not found in the controller specified, it tries to find it in shared/
        ''' </summary>
        ''' <param name="Controller">The folder name within the view</param>
        ''' <param name="Action">The control name within the view</param>
        ''' <returns>The full path to the control</returns>
        ''' <remarks></remarks>
        Public Shared Function ControlPath(ByVal Controller As String, ByVal Action As String) As String
            Dim controlName As String = IIf(Action = String.Empty, "default.ascx", Action & ".ascx")
            Dim folderName As String = IIf(Controller = String.Empty, "home/", Controller & "/")

            Dim cPath As String = App.ViewsPath & folderName & controlName
            Dim sPath As String = App.ViewsPath & "Shared/" & controlName

            ' Return the error view path
            If Controller.ToLower = "shared" AndAlso Action.ToLower = "error" Then
                Return App.ErrorViewPath
            End If

            If IO.File.Exists(App.MVCContext.MapPath(cPath)) Then
                Return cPath
            ElseIf IO.File.Exists(App.MVCContext.MapPath(sPath)) Then
                Return sPath
            Else
                Throw New ApplicationException(String.Format("Unable to find control. Search paths: {0} and {1}", _
                                                             cPath, sPath))
            End If
        End Function

        ''' <summary>
        ''' Returns the path to the control being currently requested.
        ''' </summary>
        Public Shared Function ControlPath() As String
            Dim route As RouteInfo = App.MVCContext.Route
            Return ControlPath(route.Controller, route.Action)
        End Function


        ''' <summary>
        ''' Executes the current requested action
        ''' </summary>
        Public Shared Function ExecuteAction() As ActionResult
            Dim route As RouteInfo = App.MVCContext.Route
            Return ExecuteAction(route.Controller, route.Action)
        End Function

        ''' <summary>
        ''' Executes an action on a controller
        ''' </summary>
        ''' <param name="ControllerName">Name of the controller</param>
        ''' <param name="Action">Name of the action</param>
        ''' <returns>A baseview control</returns>
        Public Shared Function ExecuteAction(ByVal ControllerName As String, ByVal Action As String) As ActionResult

            ' Cache key for the controller and action
            Dim controllerKey = ControllerName.ToLower & "typecache"
            Dim actionKey = ControllerName.ToLower & "." & Action.ToLower

            ' CONTROLLER
            '   Get the type from cache, if not there add it
            Dim objT As Type = App.Cache.GetValue(controllerKey)
            If objT Is Nothing Then
                Dim typename = App.ModuleNamespace & "." & ControllerName & "Controller"
                objT = ActionAssembly.GetType(typename, True, True)
                If objT IsNot Nothing Then
                    App.Cache.Add(controllerKey, objT)
                End If
            End If
            '   Return nothing if the controller is not found
            If objT Is Nothing Then Return Nothing
            '   Construct the controller
            Dim objController = objT.InvokeMember("ctor", BindingFlags.CreateInstance, Nothing, Nothing, Nothing)   'Dim objController = objT.InvokeMember("ctor", BindingFlags.CreateInstance, Nothing, Nothing, New Object() {page})

            ' ACTION
            '   Get the method from cache, if not there find it and add it
            Dim cachedMethod As MethodInfo = App.Cache.GetValue(actionKey)
            If cachedMethod Is Nothing Then
                For Each m In objT.GetMethods
                    If m.Name.ToLower = Action.ToLower Then
                        App.Cache.Add(actionKey, m)
                        cachedMethod = m
                    End If
                Next
            End If
            '   Return nothign if the method was not found
            If cachedMethod Is Nothing Then Return Nothing
            '   Execute the action
            Return cachedMethod.Invoke(objController, Nothing)

        End Function


        Public Shared Function RenderPartial(ByVal ControllerName As String, ByVal ViewName As String) As String
            Return RenderPartial(ControllerName, ViewName, Nothing)
        End Function

        Public Shared Function RenderPartial(ByVal ControllerName As String, ByVal ViewName As String, ByVal data As Object) As String
            ' Load the control
            Dim ctlPath = RouteHelper.ControlPath(ControllerName, ViewName)
            Dim pageHolder = New Page
            Dim viewControl As BaseView = pageHolder.LoadControl(ctlPath)

            ' Execute the control
            Dim t = viewControl.GetType
            If data IsNot Nothing Then
                Dim modelData = t.GetField("ModelData")
                If modelData IsNot Nothing Then
                    modelData.SetValue(viewControl, data, BindingFlags.SetField, Nothing, Nothing)
                End If
            End If

            ' Set the IsRenderPartial 
            Dim IsRenderPartial = t.GetField("IsRenderPartial")
            If IsRenderPartial IsNot Nothing Then
                IsRenderPartial.SetValue(viewControl, True, BindingFlags.SetField, Nothing, Nothing)
            End If

            ' Load the control on the page
            pageHolder.Controls.Add(viewControl)

            ' Render the page
            Return App.MVCContext.ExecutePage(pageHolder)
        End Function



#Region "LoadView / RenderView / RenderJson / RenderLiteral"

        Public Shared Sub RenderView(ByVal viewPath As String, ByVal data As Object)
            ' Load the control
            Dim pageHolder = New Page
            Dim viewControl As BaseView = pageHolder.LoadControl(viewPath)

            ' Execute the control
            Dim t = viewControl.GetType
            If data IsNot Nothing Then
                Dim modelData = t.GetField("ModelData")
                If modelData IsNot Nothing Then
                    modelData.SetValue(viewControl, data, BindingFlags.SetField, Nothing, Nothing)
                End If
            End If

            ' Set the IsRenderPartial 
            Dim IsRenderPartial = t.GetField("IsRenderPartial")
            If IsRenderPartial IsNot Nothing Then
                IsRenderPartial.SetValue(viewControl, True, BindingFlags.SetField, Nothing, Nothing)
            End If

            ' Load the control on the page
            pageHolder.Controls.Add(viewControl)

            ' Render the page
            App.MVCContext.Response.Write(App.MVCContext.ExecutePage(pageHolder))
        End Sub

        Public Shared Function LoadView(ByVal viewPath As String, ByVal data As Object, ByVal currentPage As Page) As UserControl
            Dim ctl = currentPage.LoadControl(viewPath)

            ' Set the model data if present in the control
            If data IsNot Nothing Then
                Dim t = ctl.GetType
                Dim modelData = t.GetField("ModelData")
                If modelData IsNot Nothing Then
                    modelData.SetValue(ctl, data, BindingFlags.SetField, Nothing, Nothing)
                End If
            End If

            Return ctl
        End Function

        Public Shared Sub RenderJson(ByVal data As String)
            App.MVCContext.Response.Clear()
            SetNoCache()
            App.MVCContext.Response.ContentType = "application/json"
            App.MVCContext.Response.Write(data)
            App.MVCContext.Response.End()
        End Sub

        Public Shared Sub RenderLiteral(ByVal data As String)
            App.MVCContext.Response.Clear()
            SetNoCache()
            App.MVCContext.Response.Write(data)
            App.MVCContext.Response.End()
            Return
        End Sub

        Private Shared Sub SetNoCache()
            App.MVCContext.Response.Expires = 0
            App.MVCContext.Response.Cache_SetNoServerCaching()
            App.MVCContext.Response.Cache_SetNoStore()
            App.MVCContext.Response.AppendHeader("Pragma", "no-cache")
        End Sub

#End Region


    End Class

End Namespace
